/*
 * @Description: Description
 * @Author: Dryad
 * @Date: 2019-09-15 07:58:37
 * @LastEditors: Dryad
 * @LastEditTime: 2019-10-19 16:16:52
 * @Encoding: utf-8
 */
#include <rtthread.h>
#include <rthw.h>
#include <rtdevice.h>
#include <stm32f4xx_tim.h>
#include <misc.h>
#include "./modbusrtu_master.h"
#include "../modbus_CRC16.h"

#define MB_MASTER_DELAY_CONVERT_DEFAULT 300   /* 默认广播转换延时时间，单位为系统时钟 */
#define MB_MASTER_TIMEOUT_RESPOND_DEFAULT 200 /* 默认应答超时时间，单位为系统时钟 */

#define LOG_TAG "modbus rtu master" /* 该模块TAG */
#define LOG_LVL LOG_LVL_DBG         /* 静态过滤级别为调试级 */
#include <ulog.h>                   /* 必须放在宏定义下面 */

/* 通信用相关串口和定时器等 */
static rt_size_t rx_length = 0, tx_length = 0;   /* 接收,发送数据长度 */
static char rx_data[512] = {0};                  /* Modbus主机接收数据暂存 */
static char tx_data[512] = {0};                  /* Modbus主机发送数据暂存 */
static struct rt_semaphore modbus_master_rx_sem; /* 接收到应答数据信号量 */
static TIM_TypeDef *const tim = TIM10;           /* 超时检测定时器为TIM10 */
static rt_device_t modbus_master_dev = RT_NULL;  /* 串口设备句柄 */

static struct rt_mutex master_mutex; /* 主机串口互斥量 */

static rt_err_t Modbus_Master_RX_CallBack(rt_device_t dev, rt_size_t size); /* Modbus Master接收回调函数 */

rt_inline void write_us(const unsigned short data) RT_UNUSED;
static void write_s(const short data) RT_UNUSED;
rt_inline void write_uc(const unsigned char data) RT_UNUSED;
static void write_c(const char data) RT_UNUSED;

static unsigned char mb_slave_addr = 0; /* 从机地址 */
static unsigned char func_code = 0;     /* 功能码 */
static int rx_data_frame_length = 0;    /* 接收数据帧长度 */
static int func_data_length = 0;        /* 接收的寄存器数据长度 */
static const char *func_data = RT_NULL; /* 数据域 */

static rt_size_t send_data(void) RT_USED;                            /* 发送数据 */
static void clear_rx_buf(rt_int32_t time);                           /* 清空串口接收缓存区 */
static Modbus_Error_Code Wait_Response(rt_size_t tx_length);         /* 等待应答 */
static rt_err_t Parse_RX(const char *rx, const rt_size_t rx_length); /* 解析接收到的数据 */

void MR_Master_Init(unsigned long baudrate)
{
    modbus_master_dev = rt_device_find(ModbusRTU_Master_DEVICE_NAME); /* ModbusRTU Master设备名称 */

    if (modbus_master_dev == RT_NULL)
    {
        rt_kprintf("Modbus Matser Uart Error\n");
        return;
    }

    tx_length = rx_length = 0;

    /* 设置波特率 */
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    config.baud_rate = baudrate;
    config.bufsz = 512;
    rt_device_control(modbus_master_dev, RT_DEVICE_CTRL_CONFIG, &config);

    rt_device_open(modbus_master_dev, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX); /*中断发送，中断接收打开串口*/

    /* 初始化Modbus Slave接收数据信号量，初始值为0，接收到数据后释放信号量 */
    rt_sem_init(&modbus_master_rx_sem, "modbus rtu master rx sem", 0, RT_IPC_FLAG_FIFO);
    rt_device_set_rx_indicate(modbus_master_dev, Modbus_Master_RX_CallBack);

    rt_mutex_init(&master_mutex, "modbus rtu master mutex", RT_IPC_FLAG_PRIO);
    rt_mutex_take(&master_mutex, RT_WAITING_NO); /* 等待总线空闲 */

    /* 设置Modbus数据帧检测定时器 */
    {
        rt_uint16_t usTimer_T35 = 0;
        /* 根据不同波特率设置t3.5时间 */
        if (baudrate > 19200)
        {
            usTimer_T35 = 35; /* 波特率大于19200，超时时间为1.75ms */
        }
        else
        {
            /* The timer reload value for a character is given by:
		    *
		    * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
		    *             = 11 * Ticks_per_1s / Baudrate
		    *             = 220000 / Baudrate
		    * The reload for t3.5 is 1.5 times this value and similary
		    * for t3.5.
		    */
            usTimer_T35 = (7UL * 220000UL) / (2UL * baudrate); /* 波特率小于等于19200，超时时间为3.5个字符 */
        }

        TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        /* 设置定时器时基20KHz */
        TIM_TimeBaseStructure.TIM_Period = usTimer_T35 - 1;
        TIM_TimeBaseStructure.TIM_Prescaler = 8400 - 1;
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
        TIM_TimeBaseInit(tim, &TIM_TimeBaseStructure);

        tim->CR1 &= ~(1 << 1);    /* 清空UDIS,使能更新中断UEV */
        tim->CR1 |= (1 << 2);     /* 置位URS,只有计数器上溢会生成UEV（更新中断） */
        tim->SR = ~TIM_IT_Update; /* 清零更新中断标志 */

        tim->DIER |= TIM_IT_Update; /* 使能更新中断 */

        NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; /* 抢占优先级 */
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        /* 响应优先级*/
        NVIC_Init(&NVIC_InitStructure);
    }

    rt_thread_delay(100);    /* 延时100ms */
    tim->CNT = 0;            /* 计数器清0 */
    tim->CR1 |= TIM_CR1_CEN; /* 使能定时器 */
    clear_rx_buf(100);       /* 清空串口缓存区 */

    LOG_I("Modbus RTU Master初始化完毕.");
    rt_mutex_release(&master_mutex); /* 总线空闲，释放总线 */
}

rt_err_t Modbus_Master_RX_CallBack(rt_device_t dev, rt_size_t size)
{
    /* 保存接收数据长度 */
    rx_length = size;
    tim->CNT = 0;            /* 计数器清0 */
    tim->CR1 |= TIM_CR1_CEN; /* 使能定时器 */
    return RT_EOK;
}

void write_us(const unsigned short data)
{
    write_s(data);
}

void write_s(const short data)
{
    tx_data[tx_length] = data >> 8;
    tx_length++;
    tx_data[tx_length] = data & 0x00FF;
    tx_length++;
}

void write_uc(const unsigned char data)
{
    write_c(data);
}

void write_c(const char data)
{
    tx_data[tx_length] = data;
    tx_length++;
}

rt_size_t send_data(void)
{
    unsigned short temp = CRC16_Cal((const unsigned char *)tx_data, tx_length); /* 计算CRC校验 */
    write_uc(temp & 0x00FF);
    write_uc(temp >> 8);                                              /* 写入校验值 */
    return rt_device_write(modbus_master_dev, 0, tx_data, tx_length); /* 发送数据 */
}

void clear_rx_buf(rt_int32_t time)
{
    while (RT_EOK == rt_sem_take(&modbus_master_rx_sem, time))
    {
        /* 接收到了数据 */
        rt_device_read(modbus_master_dev, 0, rx_data, sizeof(rx_data)); /* 清空接收数据缓冲区 */
    }
}

Modbus_Error_Code Wait_Response(rt_size_t tx_length)
{
    if (mb_slave_addr)
    {
        /* 不为广播，等待从机应答 */
        rt_err_t rt_result = rt_sem_take(&modbus_master_rx_sem, MB_MASTER_TIMEOUT_RESPOND_DEFAULT);
        if (-RT_ETIMEOUT == rt_result)
        {
            /* 从机无应答 */
            return Modbus_Master_NoRespond; /* 无应答 */
        }

        rt_size_t read_size = rt_device_read(modbus_master_dev, 0, rx_data, rx_length); /* 读取数据 */
        rt_result = Parse_RX(rx_data, read_size);                                       /* 解析数据 */
        if (RT_EOK == rt_result)
        {
            return Modbus_Master_OK;
        }
        else
        {
            ulog_hexdump("tx_buf", 16, (rt_uint8_t *)tx_data, tx_length);
            ulog_hexdump("rx_buf", 16, (rt_uint8_t *)rx_data, read_size);
            return Modbus_Master_Frame_Error;
        }
    }

    else
    {
        /* 广播 */
        rt_thread_delay(MB_MASTER_DELAY_CONVERT_DEFAULT); /* 延时一定时间 */
        return Modbus_Master_OK;                          /* 正常 */
    }
}

/**
 * @description: 解析接收到的数据帧
 * @param {type} 
 * @return: 
 */
rt_err_t Parse_RX(const char *rx, const rt_size_t rx_length)
{
    int min_length = rx_length - rx_data_frame_length;

    for (int i = 0; i <= min_length; i++)
    {
        if (rx[0] == mb_slave_addr)
        {
            /* 检测到帧头 */
            if (rx[1] == func_code)
            {
                /* 功能码正确 */
                unsigned crc_temp = CRC16_Cal((const unsigned char *)rx, rx_data_frame_length); /* 计算CRC16校验 */
                if (0 == crc_temp)
                {
                    /* 校验通过 */
                    switch (func_code)
                    {
                    case 0x01: /* 读线圈 */
                    case 0x03: /* 读保持寄存器 */
                    {
                        if (rx[2] == func_data_length)
                        {
                            /* 数据域长度正确 */
                            func_data = &rx[3]; /* 保存数据域 */
                            return RT_EOK;
                        }
                        else
                        {
                            return -RT_ERROR;
                        }
                    }
                    case 0x05: /* 写单线圈 */
                    case 0x06: /* 写单个保持寄存器 */
                    case 0x0F: /* 写多线圈 */
                    case 0x10: /* 写多个保持寄存器 */
                    {
                        func_data = &rx[2]; /* 保存数据域 */
                        return RT_EOK;
                    }
                    default:
                        break;
                    }
                }
            }
            else if (rx[1] == (func_code + 0x80))
            {
                /* 差错码 */
                unsigned crc_temp = CRC16_Cal((const unsigned char *)rx, 5);
                if (0 == crc_temp)
                {
                    /* 校验通过 */
                    LOG_W("发送数据错误,差错码%#X,异常码%#X.", rx[1], rx[2]); /* 输出警告 */
                    return -RT_ERROR;
                }
            }
        }
        rx++;
    }

    return -RT_ERROR;
}

/**
 * @description: TIM10中断函数，用于判断Modbus RTU数据帧结束
 * @param {type}
 * @return:
 */
void TIM1_UP_TIM10_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
    if (TIM10->SR & TIM_IT_Update) //更新中断
    {
        /* 定时器更新中断 */
        TIM10->SR = ~TIM_IT_Update;
        TIM10->CR1 &= ~TIM_CR1_CEN;            /* 关闭定时器 */
        rt_sem_release(&modbus_master_rx_sem); /* 释放信号量，通知处理线程 */
    }
    /* leave interrupt */
    rt_interrupt_leave();
}

/**
 * @description: 读保持寄存器数据，功能码0x03
 * @param {type} 
 * @return: 
 */
Modbus_Error_Code MR_Read_Holding_Registers(const unsigned char slave_addr,           /* 从机地址 */
                                            const unsigned short start_register_addr, /* 寄存器起始地址 */
                                            const unsigned short register_num,        /* 寄存器数量 */
                                            unsigned short *data,                     /* 读取值写入地址 */
                                            const long mutex_timeout                  /* 等待总线空闲时间 */
)
{
    if (RT_EOK != rt_mutex_take(&master_mutex, mutex_timeout))
    {
        /* 未获取到互斥量 */
        return Modbus_Master_Busy; /* 总线忙 */
    }

    clear_rx_buf(RT_WAITING_NO); /* 清空串口缓存区 */

    mb_slave_addr = slave_addr;
    func_code = 0x03;
    func_data_length = register_num << 1;
    rx_data_frame_length = func_data_length + 5;

    write_uc(slave_addr);          /* 写入从机地址 */
    write_uc(func_code);           /* 写入功能码 */
    write_us(start_register_addr); /* 写入起始地址 */
    write_us(register_num);        /* 写入寄存器数量 */

    rt_size_t length = send_data();                 /* 发送数据 */
    Modbus_Error_Code temp = Wait_Response(length); /* 等待应答 */
    tx_length = 0;                                  /* 清除发送数据 */

    if (Modbus_Master_OK == temp)
    {
        /* 返回数据正确，保存读取到的数据 */
        int index_temp;
        for (int i = 0; i < register_num; i++)
        {
            index_temp = i << 1;
            data[i] = (func_data[index_temp] << 8) | func_data[index_temp + 1];
        }
        // rt_memcpy(data, func_data, func_data_length);
    }

    rt_mutex_release(&master_mutex); /* 释放互斥量 */
    return temp;
}

/**
 * @description: 写单个保持寄存器，功能码0x06
 * @param {type} 
 * @return: 
 */
Modbus_Error_Code MR_Writer_Holding_Register(const unsigned char slave_addr,     /* 从机地址 */
                                             const unsigned short register_addr, /* 寄存器地址 */
                                             const unsigned short data,          /* 待写入值 */
                                             const long mutex_timeout            /* 等待总线空闲时间 */
)
{
    if (RT_EOK != rt_mutex_take(&master_mutex, mutex_timeout))
    {
        /* 未获取到互斥量 */
        return Modbus_Master_Busy; /* 总线忙 */
    }

    clear_rx_buf(RT_WAITING_NO); /* 清空串口缓存区 */

    mb_slave_addr = slave_addr;
    func_code = 0x06;
    rx_data_frame_length = 8;

    write_uc(slave_addr);    /* 写入从机地址 */
    write_uc(func_code);     /* 写入功能码 */
    write_us(register_addr); /* 写入寄存器地址 */
    write_us(data);          /* 写入寄存器值 */

    rt_size_t length = send_data();                 /* 发送数据 */
    Modbus_Error_Code temp = Wait_Response(length); /* 等待应答 */
    tx_length = 0;                                  /* 清除发送数据 */

    if ((Modbus_Master_OK == temp) && (rt_memcmp(func_data, tx_data + 2, 4)))
    {
        /* 数据帧解析通过但是写入返回值出错 */
        rt_mutex_release(&master_mutex); /* 释放互斥量 */
        return Modbus_Master_Frame_Error;
    }

    rt_mutex_release(&master_mutex); /* 释放互斥量 */
    return temp;
}

/**
 * @description: 写多个保持寄存器，功能码0x10
 * @param {type} 
 * @return: 
 */
Modbus_Error_Code MR_Write_Multiple_Holding_Registers(const unsigned char slave_addr,           /* 从机地址 */
                                                      const unsigned short start_register_addr, /* 寄存器起始地址 */
                                                      const unsigned short register_num,        /* 寄存器数量 */
                                                      const unsigned short *data,               /* 待写入值地址 */
                                                      const long mutex_timeout                  /* 等待总线空闲时间 */
)
{
    if (RT_EOK != rt_mutex_take(&master_mutex, mutex_timeout))
    {
        /* 未获取到互斥量 */
        return Modbus_Master_Busy; /* 总线忙 */
    }

    clear_rx_buf(RT_WAITING_NO); /* 清空串口缓存区 */

    mb_slave_addr = slave_addr;
    func_code = 0x10;
    func_data_length = register_num << 1;
    rx_data_frame_length = 8;

    write_uc(slave_addr);          /* 写入从机地址 */
    write_uc(func_code);           /* 写入功能码 */
    write_us(start_register_addr); /* 写入寄存器起始地址 */
    write_us(register_num);        /* 写入寄存器数量 */
    write_c(func_data_length);     /* 写入字节数 */

    /* 写入寄存器数据 */
    for (int i = 0; i < register_num; i++)
    {
        write_us(*data);
        data++;
    }

    rt_size_t length = send_data();                 /* 发送数据 */
    Modbus_Error_Code temp = Wait_Response(length); /* 等待应答 */
    tx_length = 0;                                  /* 清除发送数据 */

    if ((Modbus_Master_OK == temp) && (rt_memcmp(func_data, tx_data + 2, 4)))
    {
        /* 数据帧解析通过但是返回值出错 */
        rt_mutex_release(&master_mutex); /* 释放互斥量 */
        return Modbus_Master_Frame_Error;
    }

    rt_mutex_release(&master_mutex); /* 释放互斥量 */
    return temp;
}

/**
 * @description: 读线圈，功能码0x01
 * @param {type} 
 * @return: 
 */
Modbus_Error_Code MR_Read_Coils(const unsigned char slave_addr,       /* 从机地址 */
                                const unsigned short start_coil_addr, /* 线圈起始地址 */
                                const unsigned short coil_num,        /* 线圈数量 */
                                unsigned char *data,                  /* 读取值写入地址 */
                                const long mutex_timeout              /* 等待总线空闲时间 */
)
{
    if (RT_EOK != rt_mutex_take(&master_mutex, mutex_timeout))
    {
        /* 未获取到互斥量 */
        return Modbus_Master_Busy; /* 总线忙 */
    }

    clear_rx_buf(RT_WAITING_NO); /* 清空串口缓存区 */

    mb_slave_addr = slave_addr;
    func_code = 0x01;
    func_data_length = (coil_num >> 3) + (coil_num % 8 ? 1 : 0); /* 字节数为线圈数量向上取8的整数 */
    rx_data_frame_length = func_data_length + 5;

    write_uc(slave_addr);      /* 写入从机地址 */
    write_uc(func_code);       /* 写入功能码 */
    write_us(start_coil_addr); /* 写入线圈起始地址 */
    write_us(coil_num);        /* 写入线圈数量 */

    rt_size_t length = send_data();                 /* 发送数据 */
    Modbus_Error_Code temp = Wait_Response(length); /* 等待应答 */
    tx_length = 0;                                  /* 清除发送数据 */

    if (Modbus_Master_OK == temp)
    {
        /* 返回数据正确，保存读取到的数据 */
        for (int i = 0, j = 0; i < coil_num; i++)
        {
            *data = ((*func_data) & (1 << j) ? 0xFF : 0x00);
            data++;
            j++;
            if (j >= 8)
            {
                j = 0;
                func_data++;
            }
        }
    }

    rt_mutex_release(&master_mutex); /* 释放互斥量 */
    return temp;
}

/**
 * @description: 写单线圈，功能码0x05
 * @param {type} 
 * @return: 
 */
Modbus_Error_Code MR_Write_Coil(const unsigned char slave_addr, /* 从机地址 */
                                const unsigned short coil_addr, /* 线圈地址 */
                                const unsigned char coil_data,  /* 线圈值，0x00为OFF,否则为ON */
                                const long mutex_timeout        /* 等待总线空闲时间 */
)
{
    if (RT_EOK != rt_mutex_take(&master_mutex, mutex_timeout))
    {
        /* 未获取到互斥量 */
        return Modbus_Master_Busy; /* 总线忙 */
    }

    clear_rx_buf(RT_WAITING_NO); /* 清空串口缓存区 */

    mb_slave_addr = slave_addr;
    func_code = 0x05;
    rx_data_frame_length = 8;

    write_uc(slave_addr);                  /* 写入从机地址 */
    write_uc(func_code);                   /* 写入功能码 */
    write_us(coil_addr);                   /* 写入线圈地址 */
    write_us(coil_data ? 0xFF00 : 0x0000); /* 写入线圈值 */

    rt_size_t length = send_data();                 /* 发送数据 */
    Modbus_Error_Code temp = Wait_Response(length); /* 等待应答 */
    tx_length = 0;                                  /* 清除发送数据 */

    if ((Modbus_Master_OK == temp) && (rt_memcmp(func_data, tx_data + 2, 4)))
    {
        /* 数据帧解析通过但是写入返回值出错 */
        rt_mutex_release(&master_mutex); /* 释放互斥量 */
        return Modbus_Master_Frame_Error;
    }

    rt_mutex_release(&master_mutex); /* 释放互斥量 */
    return temp;
}

/**
 * @description: 写多线圈，功能码0x0F
 * @param {type} 
 * @return: 
 */
Modbus_Error_Code MR_Write_Multiple_Coils(const unsigned char slave_addr,       /* 从机地址 */
                                          const unsigned short start_coil_addr, /* 线圈起始地址 */
                                          const unsigned short coil_num,        /* 线圈数量 */
                                          const unsigned char *data,            /* 待写入值地址 */
                                          const long mutex_timeout              /* 等待总线空闲时间 */
)
{
    if (RT_EOK != rt_mutex_take(&master_mutex, mutex_timeout))
    {
        /* 未获取到互斥量 */
        return Modbus_Master_Busy; /* 总线忙 */
    }

    clear_rx_buf(RT_WAITING_NO); /* 清空串口缓存区 */

    mb_slave_addr = slave_addr;
    func_code = 0x0F;
    func_data_length = (coil_num >> 3) + (coil_num % 8 ? 1 : 0); /* 字节数为线圈数量向上取8的整数 */
    rx_data_frame_length = 8;

    write_uc(slave_addr);      /* 写入从机地址 */
    write_uc(func_code);       /* 写入功能码 */
    write_us(start_coil_addr); /* 写入线圈起始地址 */
    write_us(coil_num);        /* 写入线圈数量 */
    write_c(func_data_length); /* 写入字节数 */

    int write_num = coil_num;
    for (int i = 0; i < func_data_length; i++)
    {
        char temp = 0;
        for (int j = 0; j < 8; j++)
        {
            if (*data != 0x00)
            {
                temp |= (1 << j);
            }
            data++;
            write_num--;
            if (write_num == 0)
            {
                break;
            }
        }
        write_c(temp);
        if (write_num == 0)
        {
            break;
        }
    }

    rt_size_t length = send_data();                 /* 发送数据 */
    Modbus_Error_Code temp = Wait_Response(length); /* 等待应答 */
    tx_length = 0;                                  /* 清除发送数据 */

    if ((Modbus_Master_OK == temp) && (rt_memcmp(func_data, tx_data + 2, 4)))
    {
        /* 数据帧解析通过但是返回值出错 */
        rt_mutex_release(&master_mutex); /* 释放互斥量 */
        return Modbus_Master_Frame_Error;
    }

    rt_mutex_release(&master_mutex); /* 释放互斥量 */
    return temp;
}
